1   /*
2    * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3    *
4    * Redistribution and use in source and binary forms, with or without
5    * modification, are permitted provided that the following conditions
6    * are met:
7    *
8    *   - Redistributions of source code must retain the above copyright
9    *     notice, this list of conditions and the following disclaimer.
10   *
11   *   - Redistributions in binary form must reproduce the above copyright
12   *     notice, this list of conditions and the following disclaimer in the
13   *     documentation and/or other materials provided with the distribution.
14   *
15   *   - Neither the name of Oracle nor the names of its
16   *     contributors may be used to endorse or promote products derived
17   *     from this software without specific prior written permission.
18   *
19   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
20   * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21   * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22   * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
23   * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24   * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25   * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26   * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27   * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28   * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30   */
31  
32  /*
33   */
34  
35  import java.awt.BorderLayout;
36  import java.awt.Font;
37  import java.awt.event.ActionEvent;
38  import java.awt.event.ActionListener;
39  import java.awt.event.ItemEvent;
40  import java.awt.event.ItemListener;
41  
42  import javax.swing.*;
43  
44  import java.util.*;
45  import java.util.regex.*;
46  
47  /**
48   * RangeMenu.java
49   *
50   * @author Shinsuke Fukuda
51   * @author Ankit Patel [Conversion to Swing - 01/07/30]
52   */
53  
54  /// Custom made choice menu that holds data for unicode range
55  
56  public final class RangeMenu extends JComboBox implements ActionListener {
57  
58      private static final int[][] UNICODE_RANGES = getUnicodeRanges();
59      private static final String[] UNICODE_RANGE_NAMES = getUnicodeRangeNames();
60  
61      private boolean useCustomRange = false;
62      private int[] customRange = { 0x0000, 0x007f };
63  
64      /// Custom range dialog variables
65      private final JDialog customRangeDialog;
66      private final JTextField customRangeStart = new JTextField( "0000", 4 );
67      private final JTextField customRangeEnd   = new JTextField( "007F", 4 );
68      private final int CUSTOM_RANGE_INDEX = UNICODE_RANGE_NAMES.length - 1;
69  
70      /// Parent Font2DTest Object holder
71      private final Font2DTest parent;
72  
73      public static final int SURROGATES_AREA_INDEX = 91;
74  
75      public RangeMenu( Font2DTest demo, JFrame f ) {
76          super();
77          parent = demo;
78  
79          for ( int i = 0; i < UNICODE_RANGE_NAMES.length; i++ )
80            addItem( UNICODE_RANGE_NAMES[i] );
81  
82          setSelectedIndex( 0 );
83          addActionListener( this );
84  
85          /// Set up custom range dialog...
86          customRangeDialog = new JDialog( f, "Custom Unicode Range", true );
87          customRangeDialog.setResizable( false );
88  
89          JPanel dialogTop = new JPanel();
90          JPanel dialogBottom = new JPanel();
91          JButton okButton = new JButton("OK");
92          JLabel from = new JLabel( "From:" );
93          JLabel to = new JLabel("To:");
94          Font labelFont = new Font( "dialog", Font.BOLD, 12 );
95          from.setFont( labelFont );
96          to.setFont( labelFont );
97          okButton.setFont( labelFont );
98  
99          dialogTop.add( from );
100         dialogTop.add( customRangeStart );
101         dialogTop.add( to );
102         dialogTop.add( customRangeEnd );
103         dialogBottom.add( okButton );
104         okButton.addActionListener( this );
105 
106         customRangeDialog.getContentPane().setLayout( new BorderLayout() );
107         customRangeDialog.getContentPane().add( "North", dialogTop );
108         customRangeDialog.getContentPane().add( "South", dialogBottom );
109         customRangeDialog.pack();
110     }
111 
112     /// Return the range that is currently selected
113 
114     public int[] getSelectedRange() {
115         if ( useCustomRange ) {
116             int startIndex, endIndex;
117             String startText, endText;
118             String empty = "";
119             try {
120                 startText = customRangeStart.getText().trim();
121                 endText = customRangeEnd.getText().trim();
122                 if ( startText.equals(empty) && !endText.equals(empty) ) {
123                     endIndex = Integer.parseInt( endText, 16 );
124                     startIndex = endIndex - 7*25;
125                 }
126                 else if ( !startText.equals(empty) && endText.equals(empty) ) {
127                     startIndex = Integer.parseInt( startText, 16 );
128                     endIndex = startIndex + 7*25;
129                 }
130                 else {
131                     startIndex = Integer.parseInt( customRangeStart.getText(), 16 );
132                     endIndex = Integer.parseInt( customRangeEnd.getText(), 16 );
133                 }
134             }
135             catch ( Exception e ) {
136                 /// Error in parsing the hex number ---
137                 /// Reset the range to what it was before and return that
138                 customRangeStart.setText( Integer.toString( customRange[0], 16 ));
139                 customRangeEnd.setText( Integer.toString( customRange[1], 16 ));
140                 return customRange;
141             }
142 
143             if ( startIndex < 0 )
144               startIndex = 0;
145             if ( endIndex > 0xffff )
146               endIndex = 0xffff;
147             if ( startIndex > endIndex )
148               startIndex = endIndex;
149 
150             customRange[0] = startIndex;
151             customRange[1] = endIndex;
152             return customRange;
153         }
154         else
155           return UNICODE_RANGES[ getSelectedIndex() ];
156     }
157 
158     /// Function used by loadOptions in Font2DTest main panel
159     /// to reset setting and range selection
160     public void setSelectedRange( String name, int start, int end ) {
161         setSelectedItem( name );
162         customRange[0] = start;
163         customRange[1] = end;
164         parent.fireRangeChanged();
165     }
166 
167     /// ActionListener interface function
168     /// ABP
169     /// moved JComboBox event code into this fcn from
170     /// itemStateChanged() method. Part of change to Swing.
171     public void actionPerformed( ActionEvent e ) {
172         Object source = e.getSource();
173 
174         if ( source instanceof JComboBox ) {
175                 String rangeName = (String)((JComboBox)source).getSelectedItem();
176 
177                 if ( rangeName.equals("Custom...") ) {
178                     useCustomRange = true;
179                     customRangeDialog.setLocationRelativeTo(parent);
180                     customRangeDialog.show();
181                 }
182                 else {
183                   useCustomRange = false;
184                 }
185                 parent.fireRangeChanged();
186         }
187         else if ( source instanceof JButton ) {
188                 /// Since it is only "OK" button that sends any action here...
189                 customRangeDialog.hide();
190         }
191     }
192 
193     private static int[][] getUnicodeRanges() {
194         List<Integer> ranges = new ArrayList<>();
195         ranges.add(0);
196         Character.UnicodeBlock currentBlock = Character.UnicodeBlock.of(0);
197         for (int cp = 0x000001; cp < 0x110000; cp++ ) {
198             Character.UnicodeBlock ub = Character.UnicodeBlock.of(cp);
199             if (currentBlock == null) {
200                 if (ub != null) {
201                     ranges.add(cp);
202                     currentBlock = ub;
203                 }
204             } else {  // being in some unicode range
205                 if (ub == null) {
206                     ranges.add(cp - 1);
207                     currentBlock = null;
208                 } else if (cp == 0x10ffff) {  // end of last block
209                     ranges.add(cp);
210                 } else if (! ub.equals(currentBlock)) {
211                     ranges.add(cp - 1);
212                     ranges.add(cp);
213                     currentBlock = ub;
214                 }
215             }
216         }
217         ranges.add(0x00);  // for user defined range.
218         ranges.add(0x7f);  // for user defined range.
219 
220         int[][] returnval = new int[ranges.size() / 2][2];
221         for (int i = 0 ; i < ranges.size() / 2 ; i++ ) {
222             returnval[i][0] = ranges.get(2*i);
223             returnval[i][1] = ranges.get(2*i + 1);
224         }
225         return returnval;
226     }
227 
228     private static String[] getUnicodeRangeNames() {
229         String[] names = new String[UNICODE_RANGES.length];
230         for (int i = 0 ; i < names.length ; i++ ) {
231             names[i] = titleCase(
232                 Character.UnicodeBlock.of(UNICODE_RANGES[i][0]).toString());
233         }
234         names[names.length - 1] = "Custom...";
235         return names;
236     }
237 
238     private static String titleCase(String str) {
239         str = str.replaceAll("_", " ");
240         Pattern p = Pattern.compile("(^|\\W)([a-z])");
241         Matcher m = p.matcher(str.toLowerCase(Locale.ROOT));
242         StringBuffer sb = new StringBuffer();
243         while (m.find()) {
244             m.appendReplacement(sb, m.group(1) + m.group(2).toUpperCase(Locale.ROOT));
245         }
246         m.appendTail(sb);
247         return sb.toString().replace("Cjk", "CJK").replace("Nko", "NKo");
248     }
249 }